#include <objidl.h>
#include "cmemio.hpp"

CMemIO::CMemIO(BYTE* p, ULONG n) :
    m_buf(p), 
    m_len(n), 
    m_pos(0)
{
}


CMemIO::~CMemIO()
{
}


HRESULT CMemIO::QueryInterface(const IID& iid, void** ppv)
{
    if (ppv == 0)
        return E_POINTER;
        
    IUnknown*& pUnk = reinterpret_cast<IUnknown*&>(*ppv);
    
    if (iid == __uuidof(IUnknown))
        pUnk = this;
        
    else if (iid == __uuidof(ISequentialStream))
        pUnk = static_cast<ISequentialStream*>(this);
        
    else
    {
        pUnk = 0;
        return E_NOINTERFACE;
    }
    
    pUnk->AddRef();
    return S_OK;
}


ULONG CMemIO::AddRef()
{
    return 1;
}


ULONG CMemIO::Release()
{
    return 1;
}


HRESULT CMemIO::Read(void* dst, ULONG cb, ULONG* pcbRead)
{
    if (pcbRead)
        *pcbRead = 0;
        
    if (cb == 0)
        return S_OK;
        
    if (m_pos >= m_len)
        return S_FALSE;
    
    if (m_buf == 0)
        return STG_E_INVALIDPOINTER;
        
    if (dst == 0)
        return STG_E_INVALIDPOINTER;
        
    const BYTE* const src = m_buf + m_pos;

    const ULONG buflen = m_len - m_pos;
    const ULONG cbRead = (cb <= buflen) ? cb : buflen;

    memcpy(dst, src, cbRead);
    m_pos += cbRead;
    
    if (pcbRead)
        *pcbRead = cbRead;
            
    return (cbRead >= cb) ? S_OK : S_FALSE;
}


HRESULT CMemIO::Write(const void* src, ULONG cb, ULONG* pcbWrite)
{
    if (pcbWrite)
        *pcbWrite = 0;
        
    if (cb == 0)
        return S_OK;
        
    if (m_pos >= m_len)
        return STG_E_MEDIUMFULL;
    
    if (m_buf == 0)
        return STG_E_INVALIDPOINTER;
        
    if (src == 0)
        return STG_E_INVALIDPOINTER;
        
    const ULONG buflen = m_len - m_pos;
    
    if (buflen < cb)  //TODO: allow partial writes?
        return STG_E_MEDIUMFULL;
        
    BYTE* const dst = m_buf + m_pos;

    memcpy(dst, src, cb);
    m_pos += cb;
    
    if (pcbWrite)
        *pcbWrite = cb;
            
    return S_OK;  //TODO: allow partial writes?
}


ULONG CMemIO::GetPosition() const
{
    return m_pos;
}
